home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / WASTE 1.2 Distribution / WASTE 1.2 / WEDrawing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-18  |  26.7 KB  |  983 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WEDrawing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing routines and other basic support functions
  6.  *
  7.  *  Copyright (c) 1993-1996 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14. #include "WASTEIntf.h"
  15.  
  16. pascal SInt32 WEOffsetToLine (SInt32 offset, WEHandle hWE)
  17. {
  18.     // given a byte offset into the text, find the corresponding line index
  19.  
  20.     WEPtr pWE = *hWE;
  21.     LineArrayPtr pLines = *pWE->hLines;
  22.     SInt32 minIndex, maxIndex, index;
  23.  
  24.     // do a fast binary search through the line array
  25.     minIndex = 0;
  26.     maxIndex = pWE->nLines;
  27.  
  28.     while (minIndex < maxIndex)
  29.     {
  30.         index = (minIndex + maxIndex) >> 1;
  31.         if (offset >= pLines[index].lineStart)
  32.         {
  33.             if (offset < pLines[index + 1].lineStart)
  34.             {
  35.                 break;
  36.             }
  37.             else
  38.             {
  39.                 minIndex = index + 1;
  40.             }
  41.         }
  42.         else
  43.         {
  44.             maxIndex = index;
  45.         }
  46.     }
  47.  
  48.     return index;
  49. }
  50.  
  51. pascal SInt32 _WEPixelToLine(SInt32 vOffset, WEHandle hWE)
  52. {
  53.     // given a vertical pixel offset in local coordinates,
  54.     // find the corresponding line index
  55.  
  56.     WEPtr pWE = *hWE;
  57.     LineArrayPtr pLines = *pWE->hLines;
  58.     SInt32 minIndex, maxIndex, index;
  59.  
  60.     // do a fast binary search through the line array
  61.     minIndex = 0;
  62.     maxIndex = pWE->nLines;
  63.  
  64.     while (minIndex < maxIndex)
  65.     {
  66.         index = (minIndex + maxIndex) >> 1;
  67.         if (vOffset >= pLines[index].lineOrigin)
  68.         {
  69.             if (vOffset < pLines[index + 1].lineOrigin)
  70.             {
  71.                 break;
  72.             }
  73.             else
  74.             {
  75.                 minIndex = index + 1;
  76.             }
  77.         }
  78.         else
  79.         {
  80.             maxIndex = index;
  81.         }
  82.     }
  83.  
  84.     return index;
  85. }
  86.  
  87. pascal SInt32 _WEOffsetToRun (SInt32 offset, WEHandle hWE)
  88. {
  89.     // given a byte offset into the text, find the corresponding style run index
  90.  
  91.     WEPtr pWE = *hWE;
  92.     RunArrayPtr pRuns = *pWE->hRuns;
  93.     SInt32 minIndex, maxIndex, index;
  94.  
  95.     // do a fast binary search through the style run array
  96.     minIndex = 0;
  97.     maxIndex = pWE->nRuns;
  98.  
  99.     while (minIndex < maxIndex)
  100.     {
  101.         index = (minIndex + maxIndex) >> 1;
  102.         if (offset >= pRuns[index].runStart)
  103.         {
  104.             if (offset < pRuns[index + 1].runStart)
  105.             {
  106.                 break;
  107.             }
  108.             else
  109.             {
  110.                 minIndex = index + 1;
  111.             }
  112.         }
  113.         else
  114.         {
  115.             maxIndex = index;
  116.         }
  117.     }
  118.  
  119.     return index;
  120. }
  121.  
  122. pascal void _WEGetIndStyle(SInt32 runIndex, WERunInfo *info, WEHandle hWE)
  123. {
  124.     WEPtr pWE = *hWE;    // assume WE record is already locked
  125.     RunArrayElementPtr pRun;
  126.  
  127.     // get a pointer to the specified run array element
  128.     pRun = *pWE->hRuns + runIndex;
  129.  
  130.     // fill in the runStart and runEnd fields from the style run array
  131.     info->runStart = pRun[0].runStart;
  132.     info->runEnd = pRun[1].runStart;
  133.  
  134.     // copy the style information from the appropriate entry in the style table
  135.     info->runAttrs = (*pWE->hStyles)[pRun->styleIndex].info;
  136. }
  137.  
  138. pascal void WEGetRunInfo(SInt32 offset, WERunInfo *info, WEHandle hWE)
  139. {
  140.     _WEGetIndStyle(_WEOffsetToRun(offset, hWE), info, hWE);
  141. }
  142.  
  143. #if WASTE_OBJECTS
  144.  
  145. pascal OSErr WEGetSelectedObject(WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  146. {
  147.     WEPtr pWE = *hWE;
  148.     WERunInfo runInfo;
  149.  
  150.     // assume current selection is not an embedded object
  151.     *hObjectDesc = nil;
  152.  
  153.     // check selection range
  154.     if (pWE->selEnd - pWE->selStart == 1)
  155.     {
  156.         // check run info
  157.         WEGetRunInfo(pWE->selStart, &runInfo, hWE);
  158.         if ((*hObjectDesc = runInfo.runAttrs.runStyle.tsObject) != nil)
  159.             return noErr;
  160.     }
  161.     return weObjectNotFoundErr;
  162. }
  163.  
  164. pascal SInt32 WEFindNextObject(SInt32 offset, WEObjectDescHandle *hObjectDesc, WEHandle hWE)
  165. {
  166.     WEPtr pWE = *hWE;
  167.     StyleTablePtr pStyles = *pWE->hStyles;
  168.     WEObjectDescHandle obj;
  169.     RunArrayElementPtr pRun;
  170.  
  171.     *hObjectDesc = nil;
  172.  
  173.     // do nothing if offset is already at the end of the text
  174.     if (offset >= pWE->textLength - 1)
  175.         return kInvalidOffset;
  176.  
  177.     // get a pointer to the run array element immediately following offset
  178.     pRun = *pWE->hRuns + _WEOffsetToRun(offset + 1, hWE);
  179.  
  180.     // perform a linear scan of the run array looking for a run whose
  181.     // corresponding style table entry points to an embedded object;
  182.     // the search will stop anyway because the last run array element has styleIndex = -1
  183.     while (pRun->styleIndex >= 0)
  184.     {
  185.         if ((obj = pStyles[pRun->styleIndex].info.runStyle.tsObject) != nil)
  186.         {
  187.             *hObjectDesc = obj;
  188.             return pRun->runStart;
  189.         }
  190.         pRun++;
  191.     }
  192.  
  193.     return kInvalidOffset;
  194.  
  195. }
  196.  
  197. #endif  // WASTE_OBJECTS
  198.  
  199. pascal void _WEContinuousStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode *mode,
  200.         WETextStyle *ts, WEHandle hWE)
  201. {
  202.     // find out which style attributes are continous over the specified text range
  203.     // on entry, *mode specifies which attributes are to be checked
  204.     // on exit, *mode specifies the continuous attributes, also copied to ts
  205.  
  206.     WEPtr pWE = *hWE;
  207.     WEStyleMode outMode;
  208.     SInt32 runIndex;
  209.     WERunInfo runInfo;
  210.  
  211.     // get style attributes to check (masking out private and unused bits)
  212.     outMode = *mode & weDoAll;
  213.  
  214.     // get style info at the beginning of the specified range
  215.     runIndex = _WEOffsetToRun(rangeStart, hWE);
  216.     _WEGetIndStyle(runIndex, &runInfo, hWE);
  217.  
  218.     // copy the specified fields to ts
  219.     _WECopyStyle(&runInfo.runAttrs.runStyle, (WETextStyle *) ts, normal, outMode | weDoReplaceFace);
  220.  
  221.     // loop through style runs across the current selection range
  222.     // if we determine that all specified attributes are discontinuous, we exit prematurely
  223.     do
  224.     {
  225.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  226.  
  227.         // determine which attributes have changed, if any
  228.         if (outMode & weDoFont)
  229.         {
  230.             if (runInfo.runAttrs.runStyle.tsFont != ts->tsFont)
  231.             {
  232.                 outMode &= (~weDoFont);
  233.             }
  234.         }
  235.         if (outMode & weDoFace)
  236.         {
  237.             if (runInfo.runAttrs.runStyle.tsFace != ts->tsFace)
  238.             {
  239.                 ts->tsFace &= runInfo.runAttrs.runStyle.tsFace;
  240.                 if (ts->tsFace == 0)
  241.                 {
  242.                     outMode &= (~weDoFace);
  243.                 }
  244.             }
  245.         }
  246.         if (outMode & weDoSize)
  247.         {
  248.             if (runInfo.runAttrs.runStyle.tsSize != ts->tsSize)
  249.             {
  250.                 outMode &= (~weDoSize);
  251.             }
  252.         }
  253.         if (outMode & weDoColor)
  254.         {
  255.             if (!_WEBlockCmp(&runInfo.runAttrs.runStyle.tsColor, &ts->tsColor, sizeof(RGBColor)))
  256.             {
  257.                 outMode &= (~weDoColor);
  258.             }
  259.         }
  260.  
  261.         runIndex++;
  262.     } while ((outMode != 0) && (runInfo.runEnd < rangeEnd));
  263.  
  264.     *mode = outMode;
  265. }
  266.  
  267. pascal void _WESynchNullStyle(WEHandle hWE)
  268. {
  269.     // This routine fills the nullStyle field of the WE record with valid information
  270.     // and makes sure that the null style font belongs to the keyboard script.
  271.  
  272.     WEPtr pWE = *hWE;    // assume WE record is already locked
  273.     SInt32 runIndex;
  274.     WERunInfo runInfo;
  275. #if !WASTE_NO_SYNCH
  276.     ScriptCode keyboardScript;
  277.     SInt16 fontID;
  278. #endif
  279.  
  280.     // find the run index of the style run preceding the insertion point
  281.     runIndex = _WEOffsetToRun(pWE->selStart - 1, hWE);
  282.  
  283.     // if the nullStyle record is marked as invalid, fill it with the style attributes
  284.     // associated with the character preceding the insertion point, and mark it as valid
  285.     if (!BTST(pWE->flags, weFUseNullStyle))
  286.     {
  287.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  288.         pWE->nullStyle = runInfo.runAttrs;
  289.         BSET(pWE->flags, weFUseNullStyle);
  290.     }
  291.  
  292. #if !WASTE_NO_SYNCH
  293.     // if only the Roman script is installed, we're finished
  294.     if (!BTST(pWE->flags, weFNonRoman))
  295.     {
  296.         return;
  297.     }
  298.  
  299.     // *** FONT / KEYBOARD SYNCHRONIZATION ***
  300.     // get the keyboard script
  301.     keyboardScript = GetScriptManagerVariable(smKeyScript);
  302.  
  303.     // find out what font will be used for the next character typed
  304.     fontID = pWE->nullStyle.runStyle.tsFont;
  305.  
  306.     // do nothing if the font script is the same as the keyboard script
  307.     if (FontToScript(fontID) == keyboardScript) return;
  308.  
  309.     // scan style runs starting from the insertion point backwards,
  310.     // looking for the first font belonging to the keyboard script
  311.     do
  312.     {
  313.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  314.         fontID = runInfo.runAttrs.runStyle.tsFont;
  315.         if (FontToScript(fontID) == keyboardScript) break;
  316.         runIndex--;
  317.     } while (runIndex>=0);
  318.  
  319.     // if no font was ever used for the keyboard script, default to the
  320.     // application font for the script
  321.     if (runIndex < 0)
  322.     {
  323.         fontID = GetScriptVariable(keyboardScript, smScriptAppFond);
  324.     }
  325.  
  326.     // change the font in the null style record
  327.     pWE->nullStyle.runStyle.tsFont = fontID;
  328. #endif
  329. }
  330.  
  331. pascal Boolean WEContinuousStyle(WEStyleMode *mode, TextStyle *ts, WEHandle hWE)
  332. {
  333.     // find out which style attributes are continous over the selection range
  334.     // on entry, the mode bitmap specifies which attributes are to be checked
  335.     // on exit, the mode bitmap specifies the continuous attributes, also copied to ts
  336.     // return true if all specified attributes are continuous
  337.  
  338.     WEPtr pWE;
  339.     WEStyleMode oldMode;
  340.     Boolean continuousStyle;
  341.     Boolean saveWELock;
  342.  
  343.     // lock the WE record
  344.  
  345.     pWE = *hWE;
  346.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  347.  
  348.     // mask out private and unused bits in mode so we don't run the risk of overwriting
  349.     // memory past the ts record (which is defined as TextStyle in the public interfaces)
  350.     *mode &= weDoAll;
  351.  
  352.     // two rather different paths are taken depending on whether
  353.     // the selection range is empty or not
  354.     if (pWE->selStart == pWE->selEnd)
  355.     {
  356.         // if the selection range is empty, always return true and set ts
  357.         // from the nullStyle record, after having validated it
  358.         continuousStyle = true;
  359.         _WESynchNullStyle(hWE);
  360.         _WECopyStyle(&pWE->nullStyle.runStyle, (WETextStyle *) ts, normal, *mode | weDoReplaceFace);
  361.     }
  362.     else
  363.     {
  364.         // otherwise get the continuous style attributes over the selection range
  365.         oldMode = *mode;
  366.         _WEContinuousStyleRange(pWE->selStart, pWE->selEnd, mode, (WETextStyle *) ts, hWE);
  367.  
  368.         // return true if mode hasn't changed
  369.         continuousStyle = (oldMode == *mode);
  370.     }
  371.  
  372.     // unlock the WE record
  373.     _WESetHandleLock((Handle) hWE, saveWELock);
  374.  
  375.     return continuousStyle;
  376. }
  377.  
  378. pascal void _WESegmentLoop(SInt32 firstLine, SInt32 lastLine, SegmentLoopProcPtr callback, void *callbackData,
  379.         WEHandle hWE)
  380. {
  381.     // For each style segment on every line in the specified range, set up
  382.     // text attributes in the port and call the callback.
  383.     // the WE record must be already locked
  384.  
  385.     WEPtr pWE = *hWE;
  386.     LinePtr pLine;
  387.     Ptr pText;
  388.     SInt32 lineIndex;
  389.     SInt32 runIndex, previousRunIndex;
  390.     SInt32 lineStart, lineEnd, segmentStart, segmentEnd;
  391.     JustStyleCode styleRunPosition;
  392.     WERunInfo runInfo;
  393.     Boolean saveLineLock;
  394.     Boolean saveTextLock;
  395.     QDEnvironment saveEnvironment;
  396.  
  397.     // save the Quickdraw environment
  398.     _WESaveQDEnvironment(pWE->port, BTST(pWE->flags, weFHasColorQD) ? true : false, &saveEnvironment);
  399.  
  400.     // make sure firstLine and lastLine are within the allowed range
  401.     lineIndex = pWE->nLines - 1;
  402.     firstLine = _WEPinInRange(firstLine, 0, lineIndex);
  403.     lastLine = _WEPinInRange(lastLine, 0, lineIndex);
  404.  
  405.     // lock the line array
  406.     saveLineLock = _WESetHandleLock((Handle) pWE->hLines, true);
  407.     pLine = *pWE->hLines + firstLine;
  408.  
  409.     // lock the text
  410.     saveTextLock = _WESetHandleLock(pWE->hText, true);
  411.     pText = *pWE->hText;
  412.  
  413.     // find the style run index corresponding to the beginning of the first line
  414.     runIndex = _WEOffsetToRun(pLine->lineStart, hWE);
  415.     previousRunIndex = -1;
  416.  
  417.     // loop thru the specified lines
  418.     for ( lineIndex = firstLine; lineIndex <= lastLine; lineIndex++ )
  419.     {
  420.         // get line start and line end
  421.         lineStart = pLine[0].lineStart;
  422.         lineEnd = pLine[1].lineStart;
  423.  
  424.         // loop thru each style run on this line
  425.         do
  426.         {
  427.             // get style run information for the current style run
  428.             _WEGetIndStyle(runIndex, &runInfo, hWE);
  429.  
  430.             if (previousRunIndex != runIndex)
  431.             {
  432.                 // set new text attributes
  433.                 TextFont(runInfo.runAttrs.runStyle.tsFont);
  434.                 TextFace(runInfo.runAttrs.runStyle.tsFace);
  435.                 TextSize(runInfo.runAttrs.runStyle.tsSize);
  436.  
  437.                 // remember previous run index
  438.                 previousRunIndex = runIndex;
  439.             }
  440.  
  441.             // determine the relative position of this style run on the line
  442.             styleRunPosition = 0;                    // onlyStyleRun
  443.  
  444.             if (runInfo.runStart <= lineStart)
  445.             {
  446.                 segmentStart = lineStart;
  447.             }
  448.             else
  449.             {
  450.                 styleRunPosition += 2;                // rightStyleRun or middleStyleRun
  451.                 segmentStart = runInfo.runStart;
  452.             }
  453.  
  454.             if (runInfo.runEnd >= lineEnd)
  455.             {
  456.                         segmentEnd = lineEnd;
  457.             }
  458.             else
  459.             {
  460.                 styleRunPosition += 1;                // leftStyleRun or middleStyleRun
  461.                 segmentEnd = runInfo.runEnd;
  462.             }
  463.  
  464.             // do the callback
  465.             if ((callback)(pLine, &runInfo.runAttrs, pText + segmentStart,
  466.                     segmentStart, segmentEnd - segmentStart, styleRunPosition, hWE, callbackData))
  467.             {
  468.                 break;
  469.             }
  470.  
  471.             // advance style run index, unless this style run goes on to the next line
  472.             if (runInfo.runEnd <= lineEnd)
  473.             {
  474.                 runIndex++;
  475.             }
  476.         } while (runInfo.runEnd < lineEnd);
  477.  
  478.         pLine++;
  479.  
  480.     }  // for
  481.  
  482.     // unlock the text
  483.     _WESetHandleLock(pWE->hText, saveTextLock);
  484.  
  485.     // unlock the line array
  486.     _WESetHandleLock((Handle) pWE->hLines, saveLineLock);
  487.  
  488.     // restore the Quickdraw environment
  489.     _WERestoreQDEnvironment(&saveEnvironment);
  490. }
  491.  
  492. pascal void _WEDrawTSMHilite(Rect *segmentRect, UInt8 tsFlags)
  493. {
  494.     SInt16 underlineHeight;
  495.     RGBColor background, foreground, saveForeground;
  496.     Boolean isColorPort;
  497.     Boolean usingTrueGray;
  498.  
  499.     isColorPort = (((CGrafPtr)(qd.thePort))->portVersion < 0);
  500.     usingTrueGray = false;
  501.  
  502.     // by default, the pen pattern is solid
  503.     PenPat(&qd.black);
  504.  
  505.     // if we're drawing in color, set the foreground color
  506.     if (isColorPort)
  507.     {
  508.         // save foreground color
  509.         GetForeColor(&saveForeground);
  510.  
  511.         // by default, the foreground color is black
  512.         foreground.red = 0;
  513.         foreground.green = 0;
  514.         foreground.blue = 0;
  515.  
  516.         // if we're underlining raw (unconverted) text, see if a "true gray" is available
  517.         if (!BTST(tsFlags, tsTSMConverted))
  518.         {
  519.             GetBackColor(&background);
  520.             usingTrueGray = GetGray(GetGDevice(), &background, &foreground);
  521.         } // if raw text
  522.  
  523.         // set the foreground color
  524.         RGBForeColor(&foreground);
  525.     } // if color graf port
  526.  
  527.     // if we're underlining raw (unconverted) text and no true gray is available,
  528.     // simulate gray with a 50% pattern
  529.     if (!BTST(tsFlags, tsTSMConverted))
  530.     {
  531.         if (!usingTrueGray)
  532.         {
  533.             PenPat(&qd.gray);
  534.         }
  535.     }
  536.     // use a 2-pixel tall underline if text is "selected", else use a 1-pixel tall underline
  537.     underlineHeight = BTST(tsFlags, tsTSMSelected) ? 2 : 1;
  538.  
  539.     // segmentRect becomes the rectangle to paint
  540.     InsetRect(segmentRect, 1, 0);
  541.     segmentRect->top = segmentRect->bottom - underlineHeight;
  542.  
  543.     // draw the underline
  544.     PaintRect(segmentRect);
  545.  
  546.     // restore the foreground color
  547.     if (isColorPort)
  548.     {
  549.         RGBForeColor(&saveForeground);
  550.     }
  551. }
  552.  
  553. static Boolean SLDraw (LinePtr pLine, const WERunAttributes *pAttrs,
  554.         Ptr pSegment, SInt32 segmentStart, SInt32 segmentLength,
  555.         JustStyleCode styleRunPosition, WEHandle hWE, void *callbackData)
  556. {
  557. #pragma unused(segmentStart)
  558.     struct SLDrawData *cd = (struct SLDrawData *) callbackData;
  559.     WEPtr pWE = *hWE;
  560.     Fixed slop;
  561.     Rect segmentRect;
  562.     RGBColor theColorBlack;
  563.  
  564.     // is this the first segment on this line?
  565.     if (IS_FIRST_RUN(styleRunPosition))
  566.     {
  567.         // calculate the line rectangle (the rectangle which completely encloses the current line)
  568.         cd->lineRect.left = pWE->destRect.left;
  569.         cd->lineRect.right = pWE->destRect.right;
  570.         cd->lineRect.top = pWE->destRect.top + pLine[0].lineOrigin;
  571.         cd->lineRect.bottom = pWE->destRect.top + pLine[1].lineOrigin;
  572.  
  573.         // calculate the visible portion of this rectangle
  574.         // we do this by intersecting the line rectangle with the view rectangle
  575.         cd->drawRect = (*pWE->viewRgn)->rgnBBox;
  576.         SectRect(&cd->lineRect, &cd->drawRect, &cd->drawRect);
  577.  
  578.         if (cd->usingOffscreen)
  579.         {
  580.             // calculate the boundary rectangle for the offscreen buffer
  581.             // this is simply drawRect converted to global coordinates
  582.             cd->bounds = cd->drawRect;
  583.             LocalToGlobal((Point *) &cd->bounds.top);
  584.             LocalToGlobal((Point *) &cd->bounds.bottom);
  585.  
  586.             // update the offscreen graphics world for the new bounds (this could fail)
  587.             cd->drawingOffscreen = false;
  588.             if (UpdateGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd->bounds, nil, nil, 0) >= 0)
  589.             {
  590.                 // NOTE: when running on a 68000 machine with the original Quickdraw,
  591.                 // a GWorld is just an extended GrafPort, and GetGWorldPixMap actually
  592.                 // returns a handle to a _copy_ of the GrafPort portBits (a BitMap, not a PixMap).
  593.                 // An important side-effect of this is that when we call SetOrigin,
  594.                 // only the original portBits is offset, not the copy.
  595.                 // get the pixel map associated with the offscreen graphics world
  596.                 cd->offscreenPixels = GetGWorldPixMap((GWorldPtr) pWE->offscreenPort);
  597.  
  598.                 // lock it down
  599.                 if (LockPixels(cd->offscreenPixels))
  600.                 {
  601.                     // offscreen pixel buffer allocation was successful
  602.                     cd->drawingOffscreen = true;
  603.  
  604.                     // switch graphics world
  605.                     SetGWorld((GWorldPtr) pWE->offscreenPort, nil);
  606.  
  607.                     // synchronize the coordinate system of the offscreen port with that of the screen port
  608.                     SetOrigin(cd->drawRect.left, cd->drawRect.top);
  609.  
  610.                     // reset the offscreen clip region
  611.                     ClipRect(&cd->drawRect);
  612.                 }
  613.             } // if pixel buffer allocation was successful
  614.         } // if usingOffscreen
  615.  
  616.         // if doErase is true, erase the drawable area before drawing text
  617.         if (cd->doErase)
  618.         {
  619.             EraseRect(&cd->drawRect);
  620.         }
  621.  
  622.         // position the pen
  623.         MoveTo(cd->lineRect.left + _WECalcPenIndent(pLine->lineSlop, pWE->alignment),
  624.                cd->lineRect.top + pLine->lineAscent);
  625.     } // if first segment on line
  626.  
  627.     // if drawingOffscreen, switch thePort to the offscreen port
  628.     // and synchronize text attributes
  629.     if (cd->drawingOffscreen)
  630.     {
  631.         SetPort(pWE->offscreenPort);
  632.         TextFont(pAttrs->runStyle.tsFont);
  633.         TextFace(pAttrs->runStyle.tsFace);
  634.         TextSize(pAttrs->runStyle.tsSize);
  635.     } // if drawingOffscreen
  636.  
  637.     // get horizontal coordinate of the pen before drawing the segment
  638.     GetPen((Point *) &segmentRect.top);
  639.  
  640.     // set the foreground color
  641.     if (cd->usingColor)
  642.     {
  643.         RGBForeColor(&pAttrs->runStyle.tsColor);
  644.     }
  645.  
  646. #if WASTE_OBJECTS
  647.     if (pAttrs->runStyle.tsObject != nil)
  648.     {
  649.         _WEDrawObject(pAttrs->runStyle.tsObject);
  650.     }
  651.     else
  652. #endif
  653.     {
  654.         slop = 0;
  655.  
  656.         // calculate the "slop" (extra space) for this text segment (justified text only)
  657.         if (pWE->alignment == weJustify)
  658.         {
  659.             // if this is the last segment on the line, strip trailing spaces
  660.             if (IS_LAST_RUN(styleRunPosition))
  661.             {
  662.                 segmentLength = VisibleLength(pSegment, segmentLength);
  663.             }
  664.             // calculate how much extra space is to be applied to this text segment
  665.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  666.                     kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  667.  
  668.         } // if (alignment == weJustify)
  669.  
  670.         // draw the segment
  671.         CallWEDrawTextProc(pSegment, segmentLength, slop, styleRunPosition, hWE, pWE->drawTextHook);
  672.     }
  673.  
  674.     // get horizontal coordinate of the pen after drawing the segment
  675.     GetPen((Point *) &segmentRect.bottom);
  676.     segmentRect.bottom = cd->lineRect.bottom;
  677.  
  678.     // if this segment is in the TSM area, underline it in the appropriate way
  679.     if (BTST(pAttrs->runStyle.tsFlags, tsTSMHilite))
  680.     {
  681.         _WEDrawTSMHilite(&segmentRect, pAttrs->runStyle.tsFlags);
  682.     }
  683.  
  684.     if (cd->drawingOffscreen)
  685.     {
  686.         if (IS_LAST_RUN(styleRunPosition))
  687.         {
  688.             // after drawing offscreen the last segment,
  689.             // prepare to copy the offscreen buffer to video RAM
  690.  
  691.             // first set the graphics world to the screen port
  692.             SetGWorld((CGrafPtr) cd->screenPort, cd->screenDevice);
  693.  
  694.             // before calling CopyBits, set the foreground color to black to avoid colorization (color only)
  695.             if (cd->usingColor)
  696.             {
  697.                 theColorBlack.red = 0;
  698.                 theColorBlack.green = 0;
  699.                 theColorBlack.blue = 0;
  700.                 RGBForeColor(&theColorBlack);
  701.             }
  702.  
  703.             // copy the offscreen image of the [visible portion of the] line to the screen
  704.             CopyBits(&pWE->offscreenPort->portBits, &cd->screenPort->portBits,
  705.                      &cd->drawRect, &cd->drawRect, srcCopy, nil);
  706.  
  707.             // restore the original offscreen coordinate system and unlock the pixel image
  708.             SetPort(pWE->offscreenPort);
  709.             SetOrigin(0, 0);
  710.             if (cd->usingColor)
  711.             {
  712.                 RGBForeColor(&theColorBlack);
  713.             }
  714.             UnlockPixels(cd->offscreenPixels);
  715.  
  716.         } // if last segment
  717.  
  718.         // restore the screen port for _WESegmentLoop
  719.         SetPort(cd->screenPort);
  720.     } // if drawingOffscreen
  721.  
  722.     return false;    // keep looping
  723. }
  724.  
  725. pascal void _WEDrawLines (SInt32 firstLine, SInt32 lastLine, Boolean doErase, WEHandle hWE)
  726. {
  727.     // draw the specified range of lines
  728.     // we can safely assume that the WE record is already locked
  729.     // and the port is already set the pWE->port
  730.  
  731.     WEPtr pWE = *hWE;            // assume WE record is locked
  732.     struct SLDrawData cd;        // block of data we'll pass to SLDraw
  733.  
  734.     BLOCK_CLR(cd);
  735.     cd.doErase = doErase;
  736.     cd.usingColor = (BTST(pWE->flags, weFHasColorQD) && !BTST(pWE->features, weFInhibitColor)) ? true : false;
  737.  
  738.     // do nothing if our graphics port is not visible
  739.     if (EmptyRgn(pWE->port->visRgn))
  740.     {
  741.         return;
  742.     }
  743.  
  744.     // save graphics world
  745.     GetGWorld((GWorldPtr *) &cd.screenPort, &cd.screenDevice);
  746.  
  747.     // If doErase is true, we're drawing over old text, so we must erase each line
  748.     // before redrawing it.  But if the weFDrawOffscreen feature is enabled, we draw
  749.     // the entire line offscreen and  we copy the image right over the old line,
  750.     // without erasing it, thus achieving a very smooth drawing effect.
  751.  
  752.     if ((doErase) && BTST(pWE->features, weFDrawOffscreen))
  753.     {
  754.         // has an offscreen world already been allocated?
  755.         if (pWE->offscreenPort == nil)
  756.         {
  757.             // nope,  create one; its bounds are set initially to an arbitrary rectangle
  758.             SetRect(&cd.bounds, 0, 0, 1, 1);
  759.  
  760.             // if NewGWorld fails, it will set pWE->offscreenPort to nil
  761.             NewGWorld((GWorldPtr *) &pWE->offscreenPort, 0, &cd.bounds, nil, nil, pixPurge + noNewDevice + useTempMem);
  762.         }
  763.         cd.usingOffscreen = (pWE->offscreenPort != nil);
  764.     }
  765.  
  766.     _WESegmentLoop(firstLine, lastLine, SLDraw, &cd, hWE);
  767.  
  768.     // restore graphics world
  769.     SetGWorld((GWorldPtr) cd.screenPort, cd.screenDevice);
  770. }
  771.  
  772. pascal SInt16 _WECalcPenIndent(SInt16 slop, WEAlignment alignment)
  773. {
  774.     SInt16 retval = 0;                // left aligned or justified
  775.  
  776.     // if alignment is weFlushDefault, align according SysDirection
  777.     if (((alignment == weFlushDefault) && (GetSysDirection() != 0)) ||
  778.          (alignment == weFlushRight))
  779.     {
  780.         retval = slop;                // right aligned
  781.     }
  782.     else if (alignment == weCenter)
  783.     {
  784.         retval = slop / 2;            // centered
  785.     }
  786.  
  787.     return retval;
  788. }
  789.  
  790. pascal void _WESaveQDEnvironment(GrafPtr port, Boolean saveColor, QDEnvironment *environment)
  791. {
  792.     GetPort(&environment->envPort);
  793.     SetPort(port);
  794.     GetPenState(&environment->envPen);
  795.     PenNormal();
  796.     environment->envStyle.tsFont = port->txFont;
  797.     environment->envStyle.tsFace = port->txFace;
  798.     environment->envStyle.tsFlags = saveColor;        // remember if color was saved
  799.     environment->envStyle.tsSize = port->txSize;
  800.     if (saveColor)
  801.     {
  802.         GetForeColor(&environment->envStyle.tsColor);
  803.     }
  804.     environment->envMode = port->txMode;
  805.     TextMode(srcOr);
  806. }
  807.  
  808. pascal void _WERestoreQDEnvironment(const QDEnvironment *environment)
  809. {
  810.     SetPenState(&environment->envPen);
  811.     TextFont(environment->envStyle.tsFont);
  812.     TextFace(environment->envStyle.tsFace);
  813.     TextSize(environment->envStyle.tsSize);
  814.     TextMode(environment->envMode);
  815.     if (environment->envStyle.tsFlags)
  816.     {
  817.         RGBForeColor(&environment->envStyle.tsColor);
  818.     }
  819.     SetPort(environment->envPort);
  820. }
  821.  
  822. pascal void _WEFillFontInfo (GrafPtr port, WERunAttributes *targetStyle)
  823. {
  824.     // given a WERunAttributes record, fill in the runHeight, runAscent fields etc.
  825.     FontInfo fInfo;
  826.     QDEnvironment saveEnvironment;
  827.  
  828.     _WESaveQDEnvironment(port, false, &saveEnvironment);
  829.  
  830.     // we don't want a zero font size; although Quickdraw accepts zero to mean
  831.     // the default font size, it can cause trouble to us when we do calculations
  832.     if (targetStyle->runStyle.tsSize == 0)
  833.         targetStyle->runStyle.tsSize = 12;
  834.  
  835.     // set the text attributes
  836.     TextFont(targetStyle->runStyle.tsFont);
  837.     TextSize(targetStyle->runStyle.tsSize);
  838.     TextFace(targetStyle->runStyle.tsFace);
  839.     GetFontInfo(&fInfo);
  840.     targetStyle->runHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
  841.     targetStyle->runAscent = fInfo.ascent;
  842.  
  843.     _WERestoreQDEnvironment(&saveEnvironment);
  844. }
  845.  
  846. pascal void _WECopyStyle(const WETextStyle *sourceStyle, WETextStyle *targetStyle,
  847.                 Style offStyles, WEStyleMode mode)
  848. {
  849.     // Copy some or all of the attributes composing sourceStyle to targetStyle.
  850.     // The mode parameter determines which attributes are to be copied and how.
  851.     // If mode contains weDoToggleFace,  offStyles indicates which
  852.     // Quickdraw styles are to be turned off.
  853.  
  854.     // COPY FONT
  855.     if (mode & weDoFont)
  856.     {
  857.             targetStyle->tsFont = sourceStyle->tsFont;
  858. #if WASTE_RESOLVE_FONT_DESIGNATORS
  859.             if (targetStyle->tsFont == systemFont)
  860.                 targetStyle->tsFont = GetSysFont();
  861.             if (targetStyle->tsFont == applFont)
  862.                 targetStyle->tsFont = GetAppFont();
  863. #endif
  864.     }
  865.  
  866.     // COPY SIZE
  867.     if (mode & (weDoSize | weDoAddSize))
  868.     {
  869.         // copy size to a long variable to avoid integer overflows when doing additions
  870.         SInt32 longSize = sourceStyle->tsSize;
  871.  
  872.         // zero really means 12
  873.         if (longSize == 0)
  874.             longSize = 12;
  875.  
  876.         // if kModeAddSize is set, the source size is added to the target size,
  877.         // otherwise the source size replaces the target size outright
  878.         if (mode & weDoAddSize)
  879.         {
  880.             longSize += targetStyle->tsSize;
  881.         }
  882.         // range-check the resulting size
  883.         longSize = _WEPinInRange(longSize, kMinFontSize, kMaxFontSize);
  884.         targetStyle->tsSize = longSize;
  885.     } // if alter size
  886.  
  887.     // COPY FACE
  888.     if (mode & (weDoFace | weDoFaceMask))
  889.     {
  890.         Style sourceFace = sourceStyle->tsFace;
  891.         Style targetFace = targetStyle->tsFace;
  892.  
  893.         if (mode & weDoFaceMask)
  894.         {
  895.             // USE MASK
  896.             // if kModeFaceMask is set, copy the Quickdraw styles (tsFace field)
  897.             // using the tsFlags field as a mask specifying which bits in the tsFace
  898.             // field are to be copied.
  899.             Style sourceMask = sourceStyle->tsFlags;
  900.             targetFace = (sourceFace & sourceMask) | (targetFace & (~sourceMask));
  901.         }
  902.         else {
  903.             // IGNORE MASK
  904.             // sourceFace replaces targetFace outright if one or both of these conditions hold:
  905.             // 1. sourceFace is zero (= empty set = plain text)
  906.             // 2. the kModeReplaceFace bit is set
  907.  
  908.             if ((sourceFace == normal) || (mode & weDoReplaceFace))
  909.             {
  910.                 targetFace = sourceFace;
  911.             }
  912.             else {
  913.                 // Otherwise sourceFace is interpreted as a bitmap indicating
  914.                 // which styles are to be altered -- all other styles are left intact.
  915.                 // What exactly happens to the styles indicated in sourceFace
  916.                 // depends on whether the kModeToggleFace bit is set or clear.
  917.  
  918.                 // if kModeToggleFace is set, turn a style off if it's set in offStyles,
  919.                 // else turn it on
  920.                 if (mode & weDoToggleFace)
  921.                 {
  922.                     targetFace = (sourceFace ^ offStyles) | (targetFace & (~sourceFace));
  923.                 }
  924.                 else
  925.                 {
  926.                     // if kModeToggleFace is clear, turn on the styles specified in sourceStyle
  927.                     targetFace |= sourceFace;
  928.                 }
  929.             }
  930.         }
  931.  
  932.         // the condense and extend attributes are mutually exclusive: if one is set
  933.         // in sourceFace, remove it from targetFace
  934.         if (sourceFace & condense)
  935.         {
  936.             targetFace &= (~extend);
  937.         }
  938.         if (sourceFace & extend)
  939.         {
  940.             targetFace &= (~condense);
  941.         }
  942.  
  943.         targetStyle->tsFace = targetFace;
  944.     }
  945.  
  946.     // COPY COLOR
  947.     if (mode & weDoColor)
  948.     {
  949.         targetStyle->tsColor = sourceStyle->tsColor;
  950.     }
  951.  
  952. #if WASTE_OBJECTS
  953.     // if kModeObject is set, copy object descriptor
  954.     if (mode & weDoObject)
  955.     {
  956.         targetStyle->tsObject = sourceStyle->tsObject;
  957.     }
  958. #endif
  959.  
  960.     // always clear targetStyle->tsFlags by default
  961.     targetStyle->tsFlags = 0;
  962.  
  963.     // if kModeFlags is set, copy the tsFlags field
  964.     if (mode & weDoFlags)
  965.     {
  966.         targetStyle->tsFlags = sourceStyle->tsFlags;
  967.     }
  968. }
  969.  
  970. pascal Boolean _WEOffsetInRange(SInt32 offset, WEEdge edge, SInt32 rangeStart, SInt32 rangeEnd)
  971. {
  972.     // return true if the position specified by the pair < offset, edge >
  973.     // is within the specified range
  974.  
  975.     // if edge is kTrailingEdge, offset really refers to the preceding character
  976.     if (edge == kTrailingEdge)
  977.     {
  978.         offset--;
  979.     }
  980.     // return true iff offset is within the specified range
  981.     return ((offset >= rangeStart) && (offset < rangeEnd));
  982. }
  983.